package com.project.senven.model.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.project.senven.commons.bean.User;
import com.project.senven.commons.config.JwtConfig;
import com.project.senven.commons.handler.UserAddException;
import com.project.senven.commons.handler.UserLoginException;
import com.project.senven.commons.mapper.UserMapper;
import com.project.senven.commons.uitl.JsonResult;
import com.project.senven.commons.uitl.MD5Util;
import com.project.senven.commons.uitl.ResultTool;
import com.project.senven.model.service.UserService;
import jakarta.annotation.Resource;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.prompt.PromptTemplate;
import org.springframework.ai.document.Document;
import org.springframework.ai.reader.pdf.PagePdfDocumentReader;
import org.springframework.ai.transformer.splitter.TokenTextSplitter;
import org.springframework.ai.vectorstore.SearchRequest;
import org.springframework.ai.vectorstore.VectorStore;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Flux;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

    //操作redis的类
    @Resource
    private StringRedisTemplate stringRedisTemplate;
    //登录前台的redis的key前缀
    public static final String LOGIN_PREFIX = "user_login";

    @Resource
    private ChatClient ollamaChatClient;
    @Resource
    private VectorStore vectorStore;

    @Value("classpath:markdown-source.md")
    private org.springframework.core.io.Resource markdownResource;
    @Value("classpath:pdf-source.pdf")
    private org.springframework.core.io.Resource pdfResource;

    // 从配置文件中读取多个PDF文件路径，使用逗号分隔
    @Value("${classpath:three-pdf.pdf}")
    private String[] pdfFilePaths;


    // 注入向量存储组件
    public UserServiceImpl(VectorStore vectorStore) {
        this.vectorStore = vectorStore;
    }

    //登录
    @Override
    public JsonResult login(User user1) {
        //查询用户是否存在。调用对应的getname方法
        User user = getUsername(user1.getName());
        //对用户输入的密码进行加密
        String input_password = MD5Util.encryptMD5(user1.getPassword());
        //验证密码
        if(!Objects.equals(user.getPassword(),input_password)){
            throw new UserLoginException("密码错误");
        }
        //该用户已经登录，提示不能登陆
        ValueOperations<String, String> forValue = stringRedisTemplate.opsForValue();
        String s = forValue.get(LOGIN_PREFIX + user.getId());
        if(s != null){
            throw new UserLoginException("该账号已经登录");
        }

        //存在token令牌，加盐保密
        String token = JwtConfig.getJwtToken(user);
        ValueOperations<String,String> ops = stringRedisTemplate.opsForValue();
        ops.set(LOGIN_PREFIX + user.getId(), token,1, TimeUnit.DAYS);
        return ResultTool.success(token);
    }

    //用户注册
    @Override
    public JsonResult register(User user) {
        // 2. 检查用户名是否已存在
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        wrapper.eq("name", user.getName());
        if (this.count(wrapper) > 0) {
            return ResultTool.fail(500,"用户名已存在");
        }

        // 3. 密码加密
        String encryptedPassword = MD5Util.encryptMD5(user.getPassword());
        user.setPassword(encryptedPassword);

        // 4. 设置默认状态
        user.setStatus("0"); // 0表示正常状态

        // 5. 保存用户信息
        try {
            boolean saveResult = this.save(user);
            if (!saveResult) {
                throw new UserAddException("注册失败，请稍后再试");
            }
        } catch (Exception e) {
            throw new UserAddException("系统错误：" + e.getMessage());
        }
        /*boolean save = save(user);
        if(!save){
            throw new UserAddException("添加失败");
        }*/
        return ResultTool.success(user);
    }

    //先查询是否存在账号的方法
    public User getUsername(String name) {
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        wrapper.eq("name", name);
        wrapper.eq("status", 0);
        User user = getOne(wrapper);
        if (user == null) {
            //抛未找到异常
            throw new UserLoginException("此用户不存在");
        }
        return user;
    }

    @Override
    public void add(){
        //创建一个特有的pdf类添加知识库
        PagePdfDocumentReader pdfReader = new PagePdfDocumentReader(pdfResource);
        List<Document> documents = pdfReader.get();
        // 转换：按Token拆分
        TokenTextSplitter splitter = new TokenTextSplitter();
        List<Document> chunks = splitter.apply(documents);
        // 加载: 存储到ES向量数据库
        vectorStore.add(chunks);
    }

    @Override
    public void batchAddPdfsToVectorStore() {
    // 创建文本拆分器，可根据需要调整参数
        TokenTextSplitter splitter = new TokenTextSplitter(
                //  chunkSize：每个拆分块的最大token数
                //  chunkOverlap：块之间的重叠token数
                //  startIndex：起始索引
                //  addStartIndex：是否添加起始索引
        );

        // 存储所有处理后的文档块
        List<Document> allChunks = new ArrayList<>();

        // 遍历所有PDF文件路径
        for (String filePath : pdfFilePaths) {
            try {
                // 创建资源对象
                org.springframework.core.io.Resource pdfResource = new org.springframework.core.io.ClassPathResource(filePath);

                // 验证文件是否存在
                if (!pdfResource.exists()) {
                    System.err.println("PDF文件不存在: " + filePath);
                    continue;
                }

                // 读取PDF文件
                PagePdfDocumentReader pdfReader = new PagePdfDocumentReader(pdfResource);
                List<Document> documents = pdfReader.get();
                System.out.println("成功读取PDF文件: " + filePath + "，获取到 " + documents.size() + " 页内容");

                // 拆分文档
                List<Document> chunks = splitter.apply(documents);
                System.out.println("文件 " + filePath + " 拆分后得到 " + chunks.size() + " 个文档块");

                // 添加到总列表
                allChunks.addAll(chunks);

            } catch (Exception e) {
                System.err.println("处理PDF文件 " + filePath + " 时发生错误: " + e.getMessage());
                e.printStackTrace();
            }
        }

        // 将所有文档块添加到向量数据库
        if (!allChunks.isEmpty()) {
            vectorStore.add(allChunks);
            System.out.println("已成功将 " + allChunks.size() + " 个文档块添加到向量数据库");
        } else {
            System.out.println("没有可添加到向量数据库的文档块");
        }
    }

    @Override
    public void find() {
        List<Document> documents = vectorStore.similaritySearch("小行星");
        documents.forEach(System.out::println);
    }

    @Override
    public String delete() {
        return "";
    }

    //流式的给用户去回答问题


    @Override
    public Flux<String> stream(String question) {
        // 1. 检索向量数据库
        SearchRequest searchRequest = SearchRequest.builder()
                .query(question)
                .similarityThreshold(0.6)
                .topK(3).build();
        List<Document> documents = vectorStore.similaritySearch(searchRequest);
        // 2. 封装提示词
        String template = """
                请基于以下上下⽂回答问题：
                 {documents}
                ⽤户问题：{question}
                """;
        PromptTemplate promptTemplate = new PromptTemplate(template);
        promptTemplate.add("documents", documents.stream()
                .map(Document::getText)
                .collect(Collectors.joining("\n")));
        promptTemplate.add("question", question);
        // 3. 将封装的提示词和问题提交给大模型
        return ollamaChatClient.prompt(promptTemplate.create())
                .user(question)
                .stream()
                .content();
    }
}
